package com.tegtech.starter.security.service;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.tegtech.common.config.TegtechConfig;
import com.tegtech.common.constant.CacheKey;
import com.tegtech.common.constant.Constants;
import com.tegtech.common.utils.http.ServletUtils;
import com.tegtech.common.utils.ip.AddressUtils;
import com.tegtech.common.utils.ip.IpUtils;
import com.tegtech.module.auth.domain.LoginAgent;
import com.tegtech.module.auth.domain.LoginUser;
import com.tegtech.starter.redis.service.RedisCacheHelper;

import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * token验证处理
 *
 * @author Jun
 */
@Component
public class TokenService {
	
	// 令牌自定义标识
	@Value("${token.header}")
	private String header;

	// 令牌秘钥
	@Value("${token.secret}")
	private String secret;

	// 令牌有效期（默认3小时）
	@Value("${token.expireTime}")
	private int expireTime;
	
	// 令牌有效期不足1小时，刷新令牌（默认1小时）
	@Value("${token.refreshTime}")
	private int refreshTime;

	private static final int UNIT = 1000;

	/**
	 * 获取用户身份信息
	 *
	 * @return 用户信息
	 */
	public LoginUser getLoginUser(HttpServletRequest request) {
		// 获取请求携带的令牌
		String token = request.getHeader(header);
		if (Validator.isNotEmpty(token)) {
			Claims claims = parseToken(token);
			// 解析对应的权限以及用户信息
			String uuid = (String) claims.get(Constants.JWT_USER_KEY);
			String userKey = getTokenKey(uuid);
			LoginUser user = RedisCacheHelper.instance.get(userKey);
			return user;
		}
		return null;
	}

	/**
	 * 设置用户身份信息
	 */
	public void setLoginUser(LoginUser loginUser) {
		if (Validator.isNotNull(loginUser) && Validator.isNotEmpty(loginUser.getToken())) {
			refreshToken(loginUser);
		}
	}

	/**
	 * 删除用户身份信息
	 */
	public void delLoginUser(String token) {
		if (Validator.isNotEmpty(token)) {
			String userKey = getTokenKey(token);
			RedisCacheHelper.instance.del(userKey);
		}
	}

	/**
	 * 创建令牌
	 *
	 * @param loginUser 用户信息
	 * @return 令牌
	 */
	public String createToken(LoginUser loginUser) {
		String token = IdUtil.fastSimpleUUID();
		loginUser.setToken(token);
		refreshToken(loginUser);

		Map<String, Object> claims = new HashMap<>();
		claims.put(Constants.JWT_USER_KEY, token);
		return createToken(claims);
	}

	/**
	 * 验证令牌有效期，相差不足refreshTime，自动刷新缓存
	 * 
	 * @param token 令牌
	 */
	public void verifyToken(LoginUser loginUser) {
		long expireTime = loginUser.getExpireTime();
		long currentTime = System.currentTimeMillis();
		if (expireTime - currentTime <= refreshTime * UNIT) {
			refreshToken(loginUser);
		}
	}

	/**
	 * 刷新令牌有效期
	 *
	 * @param loginUser 登录信息
	 */
	public void refreshToken(LoginUser loginUser) {
		loginUser.setExpireTime(System.currentTimeMillis() + expireTime * UNIT);
		// 根据uuid将loginUser缓存
		String userKey = getTokenKey(loginUser.getToken());
		RedisCacheHelper.instance.set(userKey, loginUser, expireTime);
	}

	/**
	 * 获取用户登录代理信息
	 * @return
	 */
	public LoginAgent getLoginAgent() {
		HttpServletRequest request = ServletUtils.getRequest();
		UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
		String ip = IpUtils.getIpAddr(request);
		LoginAgent loginAgent = new LoginAgent();
		loginAgent.setIpaddr(ip);
		loginAgent.setLoginLocation(AddressUtils.getRealAddressByIP(ip, TegtechConfig.isAddressEnabled()));
		loginAgent.setBrowser(userAgent.getBrowser().getName());
		loginAgent.setOs(userAgent.getOs().getName());
		loginAgent.setLoginTime(System.currentTimeMillis());
		return loginAgent;
	}

	/**
	 * 从数据声明生成令牌
	 *
	 * @param claims 数据声明
	 * @return 令牌
	 */
	private String createToken(Map<String, Object> claims) {
		return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
	}

	/**
	 * 从令牌中获取数据声明
	 *
	 * @param token 令牌
	 * @return 数据声明
	 */
	private Claims parseToken(String token) {
		return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
	}

	/**
	 * 从令牌中获取用户名
	 *
	 * @param token 令牌
	 * @return 用户名
	 */
	public String getUsernameFromToken(String token) {
		return parseToken(token).getSubject();
	}

	/**
	 * 获取redis中缓存token的key
	 * @param uuid
	 * @return
	 */
	private String getTokenKey(String uuid) {
		return CacheKey.LOGIN_TOKEN_KEY + uuid;
	}
}
